﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace CSharpExt.Time
{
    public static class TimeUtils
    {
        #region Native Import

        [DllImport("Kernel32.dll")]
        private static extern int QueryPerformanceFrequency(ref Int64 lpFrequency);
        
        [DllImport("Kernel32.dll")]
        private static extern int QueryPerformanceCounter(ref Int64 lpPerformanceCount);
        
        [DllImport("kernel32.dll")]
        private static extern uint GetTickCount();

        #endregion

        #region Constants

        /// <summary>
        /// Durata, in millisecondi, di un "Thread.Sleep(1)"
        /// </summary>
        public static readonly int  ResolutionTick;

        /// <summary>
        /// Frequenza di aggiornamento del counter realtime
        /// </summary>
        public static readonly long Frequency;

        /// <summary>
        /// Valore di Environment.TickCount allo startup
        /// </summary>
        public static readonly int  StartTick;

        /// <summary>
        /// Valore del counter realtime allo startup
        /// </summary>
        public static readonly long StartCounter;

        #endregion

        #region Private Data

        private static readonly double _frequency;
        private static readonly long _error;
        private static readonly long _error1000;

        #endregion

        #region Constructor

        static TimeUtils()
        {
            QueryPerformanceCounter(ref StartCounter);
            StartTick = Environment.TickCount;
            QueryPerformanceFrequency(ref Frequency);
            _frequency = Frequency;

            long counterTick = (long) ((StartCounter+0.0)/_frequency*1000.0 + 0.5);
            _error = StartTick - counterTick;
            _error1000 = _error*1000;
            
            // calcola il tempo medio di uno sleep (1)
            int start = Environment.TickCount;
            for (int i = 0; i < 100; ++i)
            {
                Thread.Sleep(1);
            }
            ResolutionTick = (Environment.TickCount - start + 50) / 100;
        }

        #endregion

        #region Properties

        /// <summary>
        /// va da -int.MaxInt a +int.MaxInt
        /// </summary>
        public static long TickCount
        {
            get
            {
                return Environment.TickCount;
            }
        }

        /// <summary>
        /// Stesso di "Environment.TickCount", ma non dovrebbe mai diventare < 0!
        /// </summary>
        public static long TickCountNative
        {
            get
            {
                return GetTickCount();
            }
        }

        public static long TickCountMillis
        {
            get
            {
                long pc = 0;
                QueryPerformanceCounter(ref pc);
                return (long)(pc / _frequency * 1000.0 + 0.5) + _error;
            }
        }

        public static long TickCountMicros
        {
            get
            {
                long pc = 0;
                QueryPerformanceCounter(ref pc);
                return (long)(pc / _frequency * 1000000.0 + 0.5) + _error1000;
            }
        }

        #endregion
    }
    
}
